package com.coffee.attachment.excel;

import com.coffee.attachment.ExcelDocumentDTO;
import com.coffee.attachment.NotifyExcelDownloadDTO;
import com.coffee.concurrent.CoffeeExecutorManager;
import com.coffee.core.CoffeeException;
import com.coffee.core.CoffeeHeader;
import com.coffee.core.CoffeeResponseEnum;
import com.coffee.core.JSONUtils;
import com.coffee.excel.MochaExcel;
import com.coffee.excel.MochaExcelException;
import com.coffee.excel.MochaExcelSheet;
import com.coffee.minio.args.MochaPutObjectArgs;
import com.coffee.excel.MochaExportHandler;
import com.coffee.minio.MochaMinioTemplate;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;

/**
 * @author: songkui
 * @since: 2023/4/20 17:04
 */
@Service
@Slf4j
@AllArgsConstructor
public class MochaExportService {

    private final ExcelHandler excelHandler;
    private final MochaMinioTemplate mochaMinioTemplate;

    /**
     * 导出
     *
     * @param query: excel编号
     */
    public void export(MochaExportQuery query, CoffeeHeader.UserInfo userInfo) {

        MochaExportHandler<?> exportHandler = ExcelHandlerManager.getExportEndpoint(query.getExcelCode());
        if (null == exportHandler) {
            log.error("MochaExportHandler not found: excelCode: {}", query.getExcelCode());
            throw new CoffeeException(ExcelResponseEnum.ExcelExportNotHandler);
        }

        MochaExcelSheet sheet = excelHandler.findExcelSheetByCodeFile(query.getExcelCode(), query.getFields());

        if (null == sheet || CollectionUtils.isEmpty(sheet.getFieldList())) {
            throw new CoffeeException(ExcelResponseEnum.ExcelFieldNotSet);
        }

        String queryJson = null == query.getQuery() ? null : JSONUtils.toJson(query.getQuery());
        List<?> data = exportHandler.findData(queryJson, sheet.getFieldList());
        if (CollectionUtils.isEmpty(data)){
            throw new CoffeeException(ExcelResponseEnum.ExcelNotData);
        }

        //异步操作 切入 点
        CoffeeExecutorManager.getInstance().getSlowExecutor().execute(() -> toExport(data, sheet, query, userInfo));

    }


    private void toExport(List data, MochaExcelSheet sheet, MochaExportQuery query, CoffeeHeader.UserInfo userInfo){

        InputStream inputStream = null;
        try {
            inputStream = doExport(data, sheet);

            String docCode = ExcelConstant.dateRandom();
            String object = ExcelConstant.buildExportObject(query.getExcelCode(), docCode);
            String fileName = ExcelConstant.buildExportFileName(sheet.getTitle(), docCode);

            MochaPutObjectArgs args = new MochaPutObjectArgs();
            args.setBucket(ExcelConstant.ExportBucket)
                    .setObject(object);

            mochaMinioTemplate.uploadObject(args
                    .setStream(inputStream)
                    .setFileName(fileName)
                    .setCreator(userInfo.getUserCode())
            );

            ExcelDocumentDTO documentEntity = new ExcelDocumentDTO();
            documentEntity.setDocUser(userInfo.getUserId());
            documentEntity.setDocCode(docCode);
            documentEntity.setMinioBucket(ExcelConstant.ExportBucket);
            documentEntity.setMinioObject(object);
            documentEntity.setDocExte(ExcelConstant.ExcelExt);
            documentEntity.setDocTitle(sheet.getTitle());
            documentEntity.setDocType(MochaDocumentType.Export.code());
            documentEntity.setDocDescribe(sheet.getTitle());

            Long docId =  excelHandler.saveExcelDocument(documentEntity);
            if (null != docId){
                excelHandler.notifyExcelDownload(NotifyExcelDownloadDTO.builder()
                        .tenantId(userInfo.getTenantId())
                        .docId(docId)
                        .docTitle(documentEntity.getDocTitle())
                        .userId(userInfo.getUserId())
                        .build());
            }else {
                sendFailureMessage(userInfo.getUserId(), userInfo.getTenantId(), sheet.getTitle(), "Save export excel document failure");
            }
        }catch (MochaExcelException e) {
            Integer state = e.getState();
            log.error("MochaExportService exception; code: {}; message: {}", state == null ? e.getMessage() : e.getState(), e.getMessage());
            sendFailureMessage(userInfo.getUserId(), userInfo.getTenantId(),  sheet.getTitle(), e.getMessage());
        }catch (CoffeeException ce) {
            sendFailureMessage(userInfo.getUserId(), userInfo.getTenantId(),  sheet.getTitle(), ce.getState().message());
        } catch (Exception ex){
            log.error("MochaExportService exception ", ex);
            sendFailureMessage(userInfo.getUserId(), userInfo.getTenantId(),  sheet.getTitle(), "系统异常, 请联系管理员!");
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                log.error("MochaExportService 导出excel-关闭流异常：", e);
                sendFailureMessage(userInfo.getUserId(), userInfo.getTenantId(), sheet.getTitle(), "关闭导出流异常!");
            }
        }
    }


    private InputStream doExport(List data, MochaExcelSheet sheet) {
        ByteArrayOutputStream outputStream = null;
        InputStream inputStream;
        try {
            outputStream = new ByteArrayOutputStream();
            MochaExcel.exportExcel(sheet.getFieldList(), sheet.getTitle())
                    .outputStream(outputStream)
                    .doExport(data);
            byte[] bytes = outputStream.toByteArray();
            inputStream = new ByteArrayInputStream(bytes);

        } catch (MochaExcelException e) {
            Integer state = e.getState();
            log.error("ExportHandler exception;  code: {}; message: {}", state == null ? e.getMessage() : e.getState(), e.getMessage());
            throw new CoffeeException(e.getMessage());
        }catch (Exception e){
            log.error("ExportHandler exception ", e);
            throw new CoffeeException(CoffeeResponseEnum.SysException);
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                log.error("MochaExportService 导出excel-关闭流异常：", e);
            }
        }

        return inputStream;
    }


    private void sendFailureMessage(Long userId, Long tenantId, String docTitle, String exceptionInfo){
        excelHandler.notifyExcelDownload(NotifyExcelDownloadDTO.builder()
                .userId(userId)
                .tenantId(tenantId)
                .docTitle(docTitle)
                .exceptionInfo(Optional.ofNullable(exceptionInfo).orElse("导入Excel未知异常!"))
                .build());
    }

}
